// Copyright © 2021 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"camap/pkg/apollo"
	"database/sql"
	"fmt"
	"os"
	"time"

	"github.com/shiena/ansicolor"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	_ "github.com/shaxbee/go-spatialite"
)

var (
	force bool
	add   int
	srid  int
)

// buildCmd represents the build command
var buildCmd = &cobra.Command{
	Use:   "build [flags] dbfile [dbfiles...]",
	Short: "use build command to create hdmap spatial database",
	Long:  `use build command to create hdmap spatial database and preprocess.`,
	Args:  cobra.MinimumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		st := time.Now()
		w := ansicolor.NewAnsiColorWriter(os.Stdout)
		//是否启动添加srid参数
		hasAddf := false
		fAdd := cmd.Flag("add")
		if fAdd != nil {
			hasAddf = fAdd.Changed
		}

		for _, arg := range args {
			_, err := os.Stat(arg)
			if err != nil {
				fmt.Fprintf(w, "%s hdmap file(%s) stat error, details:%v\n", "\x1b[35m", arg, err)
				log.Errorf("hdmap file(%s) stat error, details: %v", err)
				continue
			}
			db, err := sql.Open("spatialite", arg)
			if err != nil {
				fmt.Fprintf(w, "%s open hdmap file(%s) failed, details:%v\n", "\x1b[35m", arg, err)
				log.Errorf("open hdmap file(%s) failed, details: %v", err)
				continue
			}
			defer db.Close()

			//初始化apollo模块的数据库驱动
			apollo.SetDB(db)
			//初始化空间库，5.0+推荐使用full
			// _, err = db.Exec("SELECT InitSpatialMetaDataFull(1)")
			// if err != nil {
			// 	log.Fatalf("初始化空间模块失败, %v", err)
			// }

			//初始化添加坐标参考系信息，IBD里面只有spatial_sys_ref表没有数据
			if hasAddf {
				//单独添加,添加完直接返回
				apollo.InsertEpsgSrids(srid)
				continue
			} else {
				//未设置该参数,此时srid应该是默认值4326,从conf里面读取要添加的参考系信息进行逐一添加
				srids := viper.GetIntSlice("spatialite.srids")
				for _, srid := range srids {
					apollo.InsertEpsgSrids(srid)
				}
			}

			st = time.Now()
			tranTest := viper.GetIntSlice("spatialite.tranTest")
			if len(tranTest) == 2 {
				// 测试坐标转换
				rs := db.QueryRow(`SELECT node_pid AS pid,astext(TRANSFORM(setsrid(geometry,?),?)) AS pt
				FROM IBD_ROADLINK_NODE LIMIT 1;`, tranTest[0], tranTest[1])
				var pid, pt string
				if err := rs.Scan(&pid, &pt); err != nil {
					fmt.Fprintf(w, "%scoordinate transform test error, details: %v\n", "\x1b[35m", err)
					log.Errorf("coordinate transform test error, details: %v", err)
					continue
				} else {
					fmt.Fprintf(w, "%scoordinate transform test completed, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())
				}
			} else {
				fmt.Fprintf(w, "%scoordinate transform test config error {(>_<)}\n", "\x1b[35m")
				log.Errorf("coordinate transform test config error {(>_<)}")
			}

			if force {
				_, err = db.Exec("DROP TABLE IF EXISTS aodx_lane_boundary;")
				if err != nil {
					fmt.Fprintf(w, "%sdelete aodx_lane_boundary table error, details: %v\n", "\x1b[35m", err)
				}
				_, err = db.Exec("DROP TABLE IF EXISTS aodx_lane_link;")
				if err != nil {
					fmt.Fprintf(w, "%sdelete aodx_lane_link table error, details: %v\n", "\x1b[35m", err)
				}
			} else {

				row := db.QueryRow(`SELECT count(*) FROM sqlite_master WHERE type="table" AND name = "aodx_lane_boundary";`)
				var cnt int
				if err := row.Scan(&cnt); err != nil {
					fmt.Fprintf(w, "%squery aodx boundaries error, details: %v\n", "\x1b[35m", err)
				}
				if cnt > 0 {
					row = db.QueryRow(`select count(*) from aodx_lane_boundary where ver > 0;`)
					if err := row.Scan(&cnt); err != nil {
						fmt.Fprintf(w, "%squery aodx boundaries error, details: %v\n", "\x1b[35m", err)
					}
					if cnt > 0 {
						fmt.Fprintf(w, "%salready build %d boundaries, please use --force to rebuild ~\n", "\x1b[33m", cnt)
						continue
					}
				}
			}
			st = time.Now()
			//创建辅助表
			err = apollo.AodxCreate()
			if err != nil {
				fmt.Fprintf(w, "%screate aodx tables failed, details: %v\n", "\x1b[35m", err)
				log.Errorf("create aodx tables failed, details: %v", err)
				continue
			}
			fmt.Fprintf(w, "%sthe aided aodx tables created, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())
			st = time.Now()
			// err = apollo.UpdateSRID(srid)
			if err != nil {
				fmt.Fprintf(w, "%supdate aodx srid(%d) failed, details: %v\n", "\x1b[35m", srid, err)
				log.Errorf("update aodx srid(%d) failed, details: %v", srid, err)
				continue
			}
			fmt.Fprintf(w, "%supdate aodx srid completed, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())

			minAzimuth := viper.GetFloat64("hdmap.minAzimuth")
			maxAzimuth := viper.GetFloat64("hdmap.maxAzimuth")
			fmt.Printf("azimuth@:%f,%f\n", minAzimuth, maxAzimuth)
			st = time.Now()
			err = apollo.UpdateSharedBoundary(minAzimuth, maxAzimuth)
			if err != nil {
				fmt.Fprintf(w, "%supdate shared boundaries failed, details: %v\n", "\x1b[35m", err)
				log.Errorf("update shared boundaries failed, details: %v", err)
				continue
			}
			fmt.Fprintf(w, "%sshared boundaries processing completed, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())
			st = time.Now()
			err = apollo.UpdateAodxInterBoundaryCurve()
			if err != nil {
				fmt.Fprintf(w, "%supdate intersect boundaries failed, details: %v\n", "\x1b[35m", err)
				log.Errorf("update intersect boundaries failed, details: %v", err)
				continue
			}
			fmt.Fprintf(w, "%sintersect boundaries processing completed, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())
			st = time.Now()
			err = apollo.UpdateAodxInterBoundaryCurve2()
			if err != nil {
				fmt.Fprintf(w, "%sintersect boundaries curve fitting failed, details: %v\n", "\x1b[35m", err)
				log.Errorf("intersect boundaries curve fitting failed, details: %v", err)
				continue
			}
			fmt.Fprintf(w, "%sintersect boundaries fitting completed, time cost: %fs ~\n", "\x1b[32m", time.Since(st).Seconds())
		}
		fmt.Fprintf(w, "%s", "\x1b[37m") //恢复命令行颜色
	},
}

func init() {
	rootCmd.AddCommand(buildCmd)
	buildCmd.Flags().IntVar(&add, "add", 4326, "add a coordinate system from epsg code~")
	buildCmd.Flags().IntVarP(&srid, "srid", "s", 4549, "set the original coordinates of the hdmap data ~")
	buildCmd.Flags().BoolVarP(&force, "force", "f", false, "force to recreate aodx tables ~")
}
